﻿using Moq;
using Moq.Language.Flow;
using Sustainalytics.ESGRatings.BusinessLogic.Computing;
using Sustainalytics.ESGRatings.Primitives;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

using Sustainalytics.ESGRatings.Computing.Entities;

using Xunit;
using Sustainalytics.ESGRatings.BusinessLogic.Computing.Exceptions;
using Sustainalytics.ESGRatings.Computing.Entities.ESG;

namespace Sustainalytics.ESGComputing.Service.Tests.GovernanceUnitTests

{


    public static class Utils
    {
        public static void ReturnsInOrder<T, TResult>(this ISetup<T, TResult> setup,
   params object[] results) where T : class
        {
            var queue = new Queue(results);
            setup.Returns(() =>
            {
                var result = queue.Dequeue();
                if (result is Exception)
                {
                    throw result as Exception;
                }
                return (TResult)result;
            });
        }
    }

    [Trait("Category", "Unit")]
    public class ESGListMappingTests : IDisposable, IClassFixture<ComputationSetFixture>
    {
        private readonly ComputationSetFixture _dataFixture;
        private readonly Mock<IESGComputeContext> _fakeESGComputeContext = new Mock<IESGComputeContext>();
        private readonly ESGSetList _sut;

        private bool _disposed;

        public ESGListMappingTests(ComputationSetFixture dataFixture)
        {
            _dataFixture = dataFixture;
            _sut = new ESGSetList();
        }

        ~ESGListMappingTests()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (_disposed)
            {
                return;
            }

            if (disposing)
            {
                // free other managed objects that implement
                // IDisposable only
            }

            // release any unmanaged objects
            // set the object references to null

            _disposed = true;
        }

        public class GivenESGSets : ESGListMappingTests
        {
            [Fact]
            public void When_Adding_BaseClass_ESGSet_Should_throw_exception()
            {
                var fakeSet = new ESGSet(_dataFixture.IndicatorsConfiguration, _dataFixture.ESGCompanyEntities,
                    x => x.IndustryGroup, StringComparer.InvariantCultureIgnoreCase, false);

                Assert.Throws<UnknownSetException>(
                    () => _sut.TryAdd(new Guid("6D85F773-9E0D-4DE0-9BB3-62AD0F7B4C70"), fakeSet));
            }

            public GivenESGSets(ComputationSetFixture dataFixture)
                : base(dataFixture)
            {
            }
        }

        public class GivenGovernanceSets : ESGListMappingTests
        {

            private readonly IComputationSet _fakeSet;
            public GivenGovernanceSets(ComputationSetFixture dataFixture) : base(dataFixture)
            {
                _fakeESGComputeContext.Setup(x => x.AddCompanySet(It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<byte[]>(), It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<byte[]>())).ReturnsInOrder(1);

                _fakeSet = new GovernanceSet(_dataFixture.IndicatorsConfiguration, _dataFixture.GovernanceCompanyEntities);

            }

            [Fact]
            public void GettingSetByGuid_should_return_a_single_GovernanceSet()
            {

                _sut.TryAdd(new Guid("6D85F773-9E0D-4DE0-9BB3-62AD0F7B4C70"), _fakeSet);

                ICollection<IComputationSet> collection;
                _sut.GetSetByGuid(new Guid("6D85F773-9E0D-4DE0-9BB3-62AD0F7B4C70"), out collection);

                Assert.Single(collection);
                Assert.IsType(typeof(GovernanceSet), collection.Single());
            }

            [Fact]
            public void GettingSetByGuid_And_Type_should_return_a_GovernanceSet_type()
            {

                _sut.TryAdd(new Guid("6D85F773-9E0D-4DE0-9BB3-62AD0F7B4C70"), _fakeSet);

                IComputationSet actual;
                _sut.GetSetByGuid(new Guid("6D85F773-9E0D-4DE0-9BB3-62AD0F7B4C70"), typeof(GovernanceSet), out actual);

                Assert.IsType(typeof(GovernanceSet), actual);
            }

            [Fact]
            public void When_Adding_A_Set_Internal_Id_should_equal_set_id_returned_by_CUDA()
            {

                _sut.TryAdd(new Guid("00000000-0000-0000-0000-000000000003"), _fakeSet);
                var actual = _fakeSet.Id;
                Assert.Equal(1, actual);
            }
        }
    }

    public class ComputationSetFixture : IDisposable
    {
        private bool _disposed;
        public List<GovernanceCompanyEntity> GovernanceCompanyEntities;
        public IndicatorsConfiguration IndicatorsConfiguration;

        public ComputationSetFixture()
        {
            var indicatorTemplates = new List<ESGRatings.Computing.Entities.IndicatorTemplate>
                {
                    new ESGRatings.Computing.Entities.IndicatorTemplate("G.1.1", "G.1"),
                    new ESGRatings.Computing.Entities.IndicatorTemplate("G.1.2", "G.1"),
                    new ESGRatings.Computing.Entities.IndicatorTemplate("G.1.3", "G.1"),
                };

            GovernanceCompanyEntities = new List<GovernanceCompanyEntity>
           {
               new GovernanceCompanyEntity(new Guid("06BD9592-E396-4F0B-A432-585435CFD17E"), "test", "test"),
           };

            ESGCompanyEntities = new List<ESGCompanyEntity>
           {
               new ESGCompanyEntity(new Guid("06BD9592-E396-4F0B-A432-585435CFD17E"), ESGRatingsOptions.CompanyAttributesEnum.Private,  "test", "test"),
           };

            IndicatorsConfiguration = new ESGIndicatorConfigurationFactory().GetBuilder()
                .GetConfiguration(indicatorTemplates);
        }

        public List<ESGCompanyEntity> ESGCompanyEntities { get; set; }

        ~ComputationSetFixture()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (_disposed)
            {
                return;
            }

            if (disposing)
            {
                // free other managed objects that implement
                // IDisposable only
            }

            // release any unmanaged objects
            // set the object references to null

            _disposed = true;
        }
    }
}